#pragma once
#include <iostream>
#include <string>
#include <time.h>
#include <vector>
#include <sstream>
#include <tuple>
#include <assert.h>
#include "message.hpp"

namespace mylog{
    class FormatItem{
    public:
        using ptr = std::shared_ptr<FormatItem>;
        FormatItem(){}
        virtual ~FormatItem(){}
        virtual void Format(std::ostream& os, const LogMsg& msg) = 0;
    };
    class MsgFormatItem : public FormatItem{
    public:
        MsgFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << msg._payload;
        }
    };
    class FileFormatItem : public FormatItem{
    public:
        FileFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << msg._file;
        }
    };
    class LineFormatItem : public FormatItem{
    public:
        LineFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << msg._line;
        }
    };
    class LevelFormatItem : public FormatItem{
    public:
        LevelFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << LogLevel::toString(msg._level);
        }
    };
    class TimeFormatItem : public FormatItem{
    public:
        std::string _fmt;
    public:
        TimeFormatItem(const std::string& str = "%H:%M:%S"): _fmt(str){
            if(_fmt.empty()){
                _fmt = "%H:%M:%S";
            }
        }
        void Format(std::ostream& os, const LogMsg& msg){
            struct tm t;
            localtime_r(&msg._time, &t);
            char tmp[128];
            strftime(tmp, sizeof tmp, _fmt.c_str(), &t);
            os << tmp;
        }
    };
    class NameFormatItem : public FormatItem{
    public:
        NameFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << msg._name;
        }
    };
    class ThreadFormatItem : public FormatItem{
    public:
        ThreadFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << msg._tid;
        }
    };
    class TableFormatItem : public FormatItem{
    public:
        TableFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << "\t";
        }
    };
    class NLineFormatItem : public FormatItem{
    public:
        NLineFormatItem(const std::string& str = ""){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << "\n";
        }
    };
    class OtherFormatItem : public FormatItem{
    public:
        std::string _str;
    public:
        OtherFormatItem(const std::string& str = ""):_str(str){}
        void Format(std::ostream& os, const LogMsg& msg){
            os << _str;
        }
    };

    class Formatter{
    public:
        using ptr = std::shared_ptr<Formatter>;
    private:
        std::string _format;
        std::vector<FormatItem::ptr> _table;
    private:
        bool Perse(){
            if(_format.empty()) return false;
            std::vector<std::tuple<std::string, std::string, int>> arry;
            std::string format_key;
            std::string format_val;
            std::string string_row;
            bool format_error = false;
            size_t pos = 0;
            while(pos < _format.size()){
                if(_format[pos] != '%'){
                    string_row.push_back(_format[pos++]);
                    continue;
                }
                if(pos + 1 < _format.size() && _format[pos + 1] == '%'){
                    string_row.push_back('%');
                    pos += 2;
                    continue;
                }
                if(string_row.empty() == false){
                    arry.push_back(std::make_tuple(string_row, "", 0));
                    string_row.clear();
                }
                pos += 1;
                if(pos < _format.size() && isalpha(_format[pos])){
                    format_key = _format[pos];
                }else{
                    std::cout << &_format[pos-1] << "位置附近格式错误！\n";
                    return false;
                }
                pos += 1;
                if(pos < _format.size() && _format[pos] == '{'){
                    format_error = true;
                    pos += 1;
                    while(pos < _format.size()){
                        if(_format[pos] == '}'){
                            format_error = false;
                            pos += 1;
                            break;
                        }
                        format_val.push_back(_format[pos++]);
                    }
                }
                arry.push_back(std::make_tuple(format_key, format_val, 1));
                format_key.clear();
                format_val.clear();
            }
            if (format_error) {
                std::cout << "{}对应出错\n";
                return false;
            }
            if(string_row.empty() == false) arry.push_back(std::make_tuple(string_row, "", 0));
            if(format_key.empty() == false) arry.push_back(std::make_tuple(format_key, format_val, 1));

            for(auto& item : arry){
                if(std::get<2>(item) == 0){
                    _table.push_back(std::make_shared<OtherFormatItem>(std::get<0>(item)));
                }
                else{
                    FormatItem::ptr fi = CreateItem(std::get<0>(item), std::get<1>(item));
                    if(fi.get() == nullptr){
                        std::cout << "没有对应的格式化字符: %" <<  std::get<0>(item) << std::endl;
                        return false;
                    }
                    _table.push_back(fi);
                }
            }
            return true;
        }

        FormatItem::ptr CreateItem(const std::string& fc, const std::string& subfmt){
            if(fc == "d") return FormatItem::ptr(new TimeFormatItem(subfmt));
            if(fc == "c") return FormatItem::ptr(new NameFormatItem(subfmt));
            if(fc == "p") return FormatItem::ptr(new LevelFormatItem(subfmt));
            if(fc == "f") return FormatItem::ptr(new FileFormatItem(subfmt));
            if(fc == "l") return FormatItem::ptr(new LineFormatItem(subfmt));
            if(fc == "m") return FormatItem::ptr(new MsgFormatItem(subfmt));
            if(fc == "t") return FormatItem::ptr(new ThreadFormatItem(subfmt));
            if(fc == "n") return FormatItem::ptr(new NLineFormatItem(subfmt));
            if(fc == "T")return FormatItem::ptr(new TableFormatItem(subfmt));
            return FormatItem::ptr();
        }
    public:
        Formatter(const std::string str = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n"): _format(str){
            assert(Perse());
        }
        void Format(std::ostream& os, const LogMsg& msg){
            for(auto ptr : _table){
                ptr->Format(os, msg);
            }
        }
        std::string Format(const LogMsg& msg){
            std::stringstream ss;
            for(auto ptr : _table){
                ptr->Format(ss, msg);
            }
            return ss.str();
        }
    };
}